package org.lisy.distributed.lock.zookeeper;

import java.io.IOException;
import java.sql.Timestamp;
import java.util.concurrent.TimeUnit;

import org.apache.curator.RetryPolicy;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.framework.recipes.leader.LeaderLatch;
import org.apache.curator.retry.ExponentialBackoffRetry;

/**
 * LeaderLatch选举基本原理
 * 1.选择一个根路径，例如"/leader_select"，多个机器同时向该根路径下创建临时顺序节点，如"/leader_latch/node_1"，"/leader_latch/node_2"
 * 2.节点编号最小(这里为 node_1)的 zk 客户端成为 leader，没抢到 Leader 的节点都监听前一个节点的删除事件，在前一个节点删除后进行重新抢主
 * 
 * @author lisy
 */
public class LeaderLatchLock {

	CuratorFramework client = null;
	LeaderLatch leaderLatch = null;
	
	public void init() {
		RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 3);
		client = CuratorFrameworkFactory.newClient("localhost:2181", retryPolicy);
		client.start();
		// 不传参与者id，默认为空字符串
		leaderLatch = new LeaderLatch(client, "/distributed/lock/leader", "");
		try {
			// 开始参与选举主节点过程
			// 阻塞方法 await(long timeout, TimeUnit unit)，等待到期后返回结果
			// 非阻塞方法 start(), 需要使用hasLeadership()循环判断
			leaderLatch.start();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	public static void main(String[] args) {
		LeaderLatchLock main = new LeaderLatchLock();
		main.init();
		
		int count = 0;
		while (true) {
			if (main.leaderLatch.hasLeadership()) {
				System.out.println(new Timestamp(System.currentTimeMillis()) + " get lock！");
			} else {
				System.out.println(new Timestamp(System.currentTimeMillis()) + " not get lock！");
			}
			if (count == 10) {
				System.out.println(new Timestamp(System.currentTimeMillis()) + " close lock！");
				try {
					main.leaderLatch.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
				break;
			}
			if (main.leaderLatch.hasLeadership()) {
				count++;
			}
			try {
				TimeUnit.SECONDS.sleep(2);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}

}
